package org.mihalovic.PES.Core.Graph.Rank;

import edu.uci.ics.jung.algorithms.scoring.AbstractIterativeScorer;
import edu.uci.ics.jung.graph.DirectedSparseGraph;



/**
 * @param <N> Node type
 * @param <E> Edge type
 * @param <T> Type of rank score value
 */
public abstract class AbstractEvaluator<N, E, T> {
    protected DirectedSparseGraph</*Node, Edge*/N, E> graph;
    protected AbstractIterativeScorer<N, E, T> evaluator;

    protected boolean stepping;
    protected double max_delta = 0.001;
    protected int iterations;
    protected int max_iterations = 100;

    /** Random jump probability */
    protected final double alpha = 0.15;
    private double maxRank=0;


    /**
     * Set highest achieved score of node of this evaluator's graph
     * @param max
     */
    protected void setMaxRank(double max) {
        maxRank=max;
    }

    /**
     * @return highest score that was assigned to node when <code>Evaluate()</code> was called last.
     */
    public double getMaxRank() {
        return maxRank;
    }


    public /*abstract*/ void SetGraph(DirectedSparseGraph<N, E> graph) {
        this.graph = graph;
        iterations = 0;
        reinitializeEvaluator(graph);
        evaluator.setTolerance(max_delta);
        evaluator.setMaxIterations(max_iterations);
    }

    protected abstract void reinitializeEvaluator(DirectedSparseGraph<N, E> graph);

    /**
     * @return underlying graph for which the ranking is performed.
     */
    public DirectedSparseGraph<N, E> getGraph() { return graph; }
    

    public double getPrecision() { return evaluator.getTolerance(); }


    /**
     * Set the largest difference between rank score
     * @param precision
     */
    public void setPrecision(double precision) {
        //evaluator.setTolerance(precision);
        max_delta = precision;
    }

    public int getMaxIterations() {
        return evaluator.getMaxIterations();
    }

    public void setMaxIterations(int iterations) {
        //evaluator.setMaxIterations(iterations);
        max_iterations = iterations - 1;
    }

    /**
     * Do full ranking until convergence or iterations limit reach.
     */
    public void Evaluate() {
        do {
            evaluator.step();
            iterations++;
        } while(!evaluator.done());

        copyRanksInsideNodes();
    }


    /**
     * Do next iteration in stepping mode. If no more iterations are needed after this step, <code>isStepping()</code>
     * will be FALSE.
     */
    public void Step() {
        evaluator.step();           //Note: before first call to step() the done() returns TRUE. Seems like sort of a bug.
        iterations++;
        copyRanksInsideNodes();
        
        stepping = !evaluator.done();
    }
    
    
    /**
     * @return FALSE if not in stepping mode or made last step. Otherwise TRUE.
     */
    public boolean isStepping() { return stepping; }



    /**
     * Number of iterations performed so far, or iterations it took until convergence.
     * 
     * @return 0 if either <code>Step()</code> or <code>Evaluate()</code> wasn't invoked yet or
     * <code>getMaxIterations()</code> if evaluation hasn't converged in given max. iterations limit.
     */
    public int getIterations() {
        return iterations;
    }


    @Override
    public abstract String toString();

    /**
     * Copy resulting rank inside into every node
     */
    protected abstract void copyRanksInsideNodes();
}
